Skip to content

fix: close audit items A3 and A4 — error detail depth and JsonObject help - #43

Merged
royklo merged 2 commits into
mainfrom
fix/error-detail-json-depth
Sep 11, 2026
Merged

fix: close audit items A3 and A4 — error detail depth and JsonObject help#43
royklo merged 2 commits into
mainfrom
fix/error-detail-json-depth

Conversation

@royklo

@royklo royklo commented Sep 11, 2026

Copy link
Copy Markdown
Owner

Two open audit items, both pre-existing on main, neither touching the public surface.

A4 — API validation errors lost the reason that explained them

When an entry in the API's errors[] array matches no known shape, the module falls back to
dumping it as JSON so something actionable still reaches the user. It dumped at -Depth 5.

That fallback runs precisely when the entry's shape is unknown, so there was no basis for
assuming the useful field sat above the cut. Past the limit PowerShell doesn't error — it
substitutes @{...}, which isn't JSON, writes a truncation warning into the caller's stream,
and drops the rest:

{"reason":"tenant not in key scope"}
→ {"L8":{"L7":{"L6":{"L5":{"L4":{"L3":"@{L2=}"}}}}}}

Now -Depth 100, matching every other ConvertTo-Json in the module.

The audit item had proposed the opposite fix — amend the contract to allow a shallow depth for
error rendering. Checking what truncation actually produces settled it the other way. Swept the
rest while here: all 30 ConvertTo-Json calls in module/Public and module/Private are now
Depth 100, and the only deliberate shallow one is the documented Format.ps1xml PolicyData
preview at Depth 3.

A3 — Get-Help never showed the JSON path

Get-InforcerReportRun, Get-InforcerReportType, Get-InforcerUser, Invoke-InforcerAssessment,
Invoke-InforcerReport and Save-InforcerReportOutput all documented -OutputType but never
demonstrated it, so Get-Help <cmdlet> -Examples gave no hint that JSON output existed.
docs/CMDLET-REFERENCE.md had covered all six for a while — only the in-shell help was short,
which is why this read as a wider docs gap than it was.

Each example follows its own file's indentation and blank-line convention (two styles are in use
across module/Public), so Get-Help renders them identically to the examples already there.

Guarded by a discovery-time scan of the source rather than a hardcoded list: every cmdlet whose
ValidateSet offers JsonObject is checked, so a new one inherits the test for free. 15 today.

Verification

Both new tests are mutation-verified — reverting each fix fails exactly the intended test and
nothing else. 467 passed, 0 failed, 2 skipped. Zero ScriptAnalyzer errors.

royklo and others added 2 commits September 11, 2026 16:57
The errors[] fallback exists to surface an entry whose shape we don't
recognise. Dumping it at -Depth 5 assumed the actionable field sits above the
cut — an assumption the fallback is, by definition, in no position to make.

Past the limit PowerShell doesn't error: it substitutes "@{...}", which is not
JSON, writes a truncation warning into the caller's stream, and drops the rest.
A nested {"reason":"tenant not in key scope"} rendered as
{"L8":{"L7":{"L6":{"L5":{"L4":{"L3":"@{L2=}"}}}}}} — the reason gone, replaced
by something unparseable.

This closes audit item A4, which had proposed the opposite resolution (amend
the contract to allow a shallow depth for error rendering). Checking what
truncation actually produces settled it the other way: the guardian rule was
right.

Swept the rest while here — all 30 ConvertTo-Json calls in module/Public and
module/Private are Depth 100, and the only deliberate shallow one is the
documented Format.ps1xml PolicyData preview at Depth 3.

Test mutation-verified against the old depth. 452/0/2.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Six cmdlets documented the parameter but never demonstrated it, so
Get-Help <cmdlet> -Examples gave no hint that JSON output existed:
Get-InforcerReportRun, Get-InforcerReportType, Get-InforcerUser,
Invoke-InforcerAssessment, Invoke-InforcerReport, Save-InforcerReportOutput.

docs/CMDLET-REFERENCE.md had covered all six for a while — only the in-shell
help was short, which is why this read as a docs gap for longer than it was.

Each example matches its own file's indentation and blank-line convention, so
Get-Help renders it identically to the examples already there (two styles are
in use across module/Public).

Guarded by a discovery-time scan of the source rather than a hardcoded list:
every cmdlet whose ValidateSet offers JsonObject is checked, so a new one
inherits the test for free. 15 today, mutation-verified.

Closes audit item A3. 467/0/2.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Copilot AI lite review requested due to automatic review settings September 11, 2026 15:28
@royklo
royklo merged commit 99308f4 into main Sep 11, 2026
4 checks passed
@royklo
royklo deleted the fix/error-detail-json-depth branch September 11, 2026 15:30

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Three JSON help examples need correction for valid GUID usage or accurate filtering behavior.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

Closes audit items A3 and A4 by preventing deep API error truncation and documenting JsonObject output in cmdlet help.

Changes:

  • Raises error serialization depth to 100.
  • Adds JSON examples and discovery-based documentation tests.
  • Updates comments, version metadata, changelog, and ignore rules.
File summaries
File Description
Tests/Consistency.Tests.ps1 Adds depth and help-documentation regression tests.
module/Public/Save-InforcerReportOutput.ps1 Adds a JsonObject help example.
module/Public/Invoke-InforcerReport.ps1 Adds a JsonObject help example.
module/Public/Invoke-InforcerAssessment.ps1 Adds a JsonObject help example.
module/Public/Get-InforcerUser.ps1 Adds a JsonObject help example.
module/Public/Get-InforcerReportType.ps1 Adds a JsonObject help example.
module/Public/Get-InforcerReportRun.ps1 Adds a JsonObject help example.
module/Private/Format-InforcerErrorDetail.ps1 Prevents deep error-detail truncation.
module/Private/ConvertTo-InforcerSettingRows.ps1 Clarifies decoding comments.
module/Private/ConvertFrom-InforcerBase64Text.ps1 Clarifies UTF-8 validation comments.
module/InforcerCommunity.psd1 Bumps the module version to 0.7.2.
CHANGELOG.md Documents the fixes.
.gitignore Ignores local demo exports.
Review details

Suppressed comments (1)

module/Public/Get-InforcerReportType.ps1:45

  • In JsonObject mode this cmdlet returns immediately with the raw catalog before applying the Key/Tag/OutputFormat filters (see the early return at lines 165–172). Therefore this example returns the full catalog, not the ActiveUserCount report type as its description claims; remove -Key or change the JSON path to apply the filter.
    Get-InforcerReportType -Key ActiveUserCount -OutputType JsonObject
    Returns the report type as a JSON string (depth 100) instead of objects.
  • Files reviewed: 12/13 changed files
  • Comments generated: 2
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Get-InforcerReportRun -IncludeOutputs
Returns every run with its outputs array embedded (1 extra API call per run).
.EXAMPLE
Get-InforcerReportRun -RunId 8842 -OutputType JsonObject
Save-InforcerReportOutput -OutputPath ./bulk
Bulk-downloads every output from every visible run.
.EXAMPLE
Save-InforcerReportOutput -RunId 8842 -OutputId 1 -OutputPath ./out -OutputType JsonObject
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants